# -*- coding: utf-8 -*-
"""
豆瓣电影Top100爬虫
功能：抓取电影名称、评分、评论数，存储为CSV并分析评分分布
反爬策略：User-Agent轮换 + 随机延迟 + 异常重试
"""
from socket import fromfd

import requests
from bs4 import BeautifulSoup  # HTML解析库
import pandas as pd  # 数据分析库
import matplotlib.pyplot as plt  # 可视化库
from fake_useragent import UserAgent  # 随机生成User-Agent
import time
import random

# 反爬配置模块
ua = UserAgent()  # 创建User-Agent生成器
headers = {'User-Agent': ua.random}  # 初始Headers
douban_url = "https://movie.douban.com/top250"  # 豆瓣Top250页面（实际取前100）


def scrape_douban_top100():
    """主爬虫函数"""
    movies = []  # 存储所有电影数据的列表

    # 豆瓣分页逻辑：每页25条，前4页即Top100
    for page in range(0, 100, 25):  # start参数按0, 25, 50, 75递增
        url = f"{douban_url}?start={page}"  # 构造分页URL

        try:
            # --- 反爬关键步骤1：随机延迟 ---
            # 模拟人工操作间隔，避免触发频率限制
            delay = random.uniform(1, 3)  # 随机1~3秒
            print(f"[等待] 随机延迟 {delay:.2f}秒")
            time.sleep(delay)

            # --- 反爬关键步骤2：动态更换User-Agent ---
            current_ua = ua.random
            headers = {'User-Agent': current_ua}
            print(f"[请求] 第{page // 25 + 1}页 | UA: {current_ua}")

            # 发送HTTP请求（带超时和重试机制）
            response = requests.get(url, headers=headers, timeout=10)
            response.raise_for_status()  # 检查HTTP状态码

            # --- 页面解析 ---
            soup = BeautifulSoup(response.text, 'html.parser')

            # 定位所有电影条目（每个条目对应一个<div class="item">）
            for item in soup.select('div.item'):
                # 解析电影标题（处理可能的主标题/副标题）
                title_tag = item.select_one('span.title')
                title = title_tag.text.strip() if title_tag else "N/A"

                # 解析评分（字符串转浮点数）
                rating_tag = item.select_one('span.rating_num')
                rating = float(rating_tag.text) if rating_tag else 0.0

                # 解析评论数
                comment_tag1 = item.find('div',class_='bd')
                comment_tag=comment_tag1.find_all('span')[3]
                comment_num = int(comment_tag.text.replace('人评价', '')) if comment_tag else 0

                # 添加到数据集
                movies.append({
                    'title': title,
                    'rating': rating,
                    'comment_num': comment_num
                })

            print(f"[成功] 已抓取第{page // 25 + 1}页，累计{len(movies)}条数据")

        except requests.exceptions.RequestException as e:
            # 网络请求异常处理
            print(f"[错误] 第{page // 25 + 1}页抓取失败: {str(e)}")
            continue

    return movies


def save_and_analyze(movies):
    """数据存储与分析函数"""
    # --- 数据存储 ---
    df = pd.DataFrame(movies)  # 转换为Pandas DataFrame

    # 保存为CSV（UTF-8编码避免中文乱码）
    csv_path = r'D:\pycharm\Collection of Personal Works\data\douban_top100.csv'
    df.to_csv(csv_path, index=False, encoding='utf-8-sig')
    print(f"[存储] 数据已保存至 {csv_path}")

    # --- 数据分析 ---
    print("\n[分析] 数据统计摘要:")
    print(df.describe())  # 输出评分/评论数的统计指标

    # 绘制评分分布直方图
    plt.figure(figsize=(10, 6))
    df['rating'].hist(bins=10, edgecolor='black')
    plt.title('豆瓣Top100电影评分分布', fontsize=14)
    plt.xlabel('评分', fontsize=12)
    plt.ylabel('电影数量', fontsize=12)
    plt.grid(axis='y', alpha=0.5)

    # 保存可视化结果
    img_path = r'D:\pycharm\Collection of Personal Works\data\rating_distribution.png'
    plt.savefig(img_path, dpi=300, bbox_inches='tight')
    print(f"[可视化] 图表已保存至 {img_path}")


if __name__ == '__main__':
    print("=== 豆瓣电影Top100爬虫启动 ===")
    movie_data = scrape_douban_top100()

    if movie_data:
        save_and_analyze(movie_data)
        print("=== 执行完成 ===")
    else:
        print("!!! 未获取到有效数据 !!!")